<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>橘猫谷子柜 | 管理端</title>
    <link
        rel="stylesheet"
        href="https://image-space.thebestxt.cc/website/css/ele-plus.css"
    />
    <link
        rel="stylesheet"
        href="https://image-space.thebestxt.cc/website/css/vant4.css"
    />
    <!-- Import Vue 3 -->
    <script src="https://image-space.thebestxt.cc/website/js/ele-plus-zh-cn.js"></script>
    <script src="https://image-space.thebestxt.cc/website/js/vue3.js"></script>
    <!-- Import component library -->
    <script src="https://image-space.thebestxt.cc/website/js/ele-plus.js"></script>
    <script src="https://image-space.thebestxt.cc/website/js/vant4.min.js"></script>
    <script src="https://image-space.thebestxt.cc/website/js/ele-plus-icon.js"></script>

    <script src="https://image-space.thebestxt.cc/website/js/pica.js"></script>

    <style>
        .el-header {
            display: flex;
            flex-direction: row;
            align-items: center;
        }
        :root {
            --van-pagination-item-default-color: #0CB85C;
            --van-checkbox-checked-icon-color: #0CB85C;
            --van-back-top-background: #0CB85C;
        }

        .add-images {
            display: flex;
            flex-direction: row;
            flex-wrap: wrap;
            justify-content: flex-start;
            align-items: center;
            align-content: center;
            margin-top: 10px;
        }
        .add-images .add-image-item {
            width: 100px;
            height: 100px;
            margin: 0 5px 5px 0;
            border-radius: 10px;
            border: solid 1px #efeff5;
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;
            font-size: small;
            color: #333;
        }
    </style>
</head>
<body>
    <div id="app" class="index-container">
        <el-container>
            <el-header> <el-link type="success" href="/goods" style="margin-right: 10px;">首页</el-link>| 橘猫谷子柜 | {{ userText }} | 第 @{ page } 页 | 共 @{ count } 个</el-header>
            <el-main>
                <!-- <el-row style="margin-bottom: 20px;">
                    <van-button type="success" @click="onAdd">添加</van-button>
                </el-row> -->
                <el-card style="margin-bottom: 10px;">
                    <template #header>
                        <el-row justify="space-between" align="middle">
                            <van-button type="success" size="small" @click="onAdd">添加</van-button>
                            <el-row>
                                <van-button style="margin-right: 10px;" type="success" plain size="small" @click="handleClearSearch">清空</van-button>
                                <van-button type="success" size="small" @click="handleSearch">搜索</van-button>
                            </el-row>
                        </el-row>
                    </template>
                    <el-collapse>
                        <el-collapse-item :title="`IP(${search.ip.length})`" name="1">
                            <van-field style="margin: 10px 0;" v-model="optionsSearch.ip" clearable placeholder="筛选IP选项"></van-field>
                            <van-checkbox-group v-model="search.ip" shape="square">
                                <van-checkbox
                                    style="margin: 0 5px 5px 0;"
                                    v-for="(v, k) in ipOptins"
                                    :key="k"
                                    :name="v.value">
                                    @{ v.label }
                                </van-checkbox>
                            </van-checkbox-group>
                        </el-collapse-item>
                        <el-collapse-item :title="`品牌(${search.brand.length})`" name="2">
                            <van-field style="margin: 10px 0;" v-model="optionsSearch.brand" clearable placeholder="筛选品牌选项"></van-field>
                            <van-checkbox-group v-model="search.brand" shape="square">
                                <van-checkbox
                                    style="margin: 0 5px 5px 0;"
                                    v-for="(v, k) in brandOptins"
                                    :key="k"
                                    :name="v.value">
                                    @{ v.label }
                                </van-checkbox>
                            </van-checkbox-group>
                        </el-collapse-item>
                        <el-collapse-item :title="`类型(${search.type.length})`" name="3">
                            <van-field style="margin: 10px 0;" v-model="optionsSearch.type" clearable placeholder="筛选类型选项"></van-field>
                            <van-checkbox-group v-model="search.type" shape="square">
                                <van-checkbox
                                    style="margin: 0 5px 5px 0;"
                                    v-for="(v, k) in typeOptins"
                                    :key="k"
                                    :name="v.value">
                                    @{ v.label }
                                </van-checkbox>
                            </van-checkbox-group>
                        </el-collapse-item>
                        <el-collapse-item :title="`角色(${search.character.length})`" name="4">
                            <van-field style="margin: 10px 0;" v-model="optionsSearch.character" clearable placeholder="筛选角色选项"></van-field>
                            <van-checkbox-group v-model="search.character" shape="square">
                                <van-checkbox
                                    style="margin: 0 5px 5px 0;"
                                    v-for="(v, k) in characterOptins"
                                    :key="k"
                                    :name="v.value">
                                    @{ v.label }
                                </van-checkbox>
                            </van-checkbox-group>
                        </el-collapse-item>
                        <el-row style="margin: 10px 0;" justify="end" align="middle">
                            <van-button style="margin-right: 10px;" type="success" plain size="small" @click="handleClearSearch">清空</van-button>
                            <van-button type="success" size="small" @click="handleSearch">搜索</van-button>
                        </el-row>
                      </el-collapse>
                </el-card>

                <van-pagination
                    style="margin-bottom: 20px;"
                    v-model="page"
                    :items-per-page="10"
                    :total-items="count"
                    force-ellipses
                    prev-text="<"
                    next-text=">"
                    @change="handlePageChange">
                </van-pagination>

                <el-card style="margin-bottom: 10px;" v-for="(v, k) in list">
                    <template #header v-if="v.title">
                        @{ v.title }
                    </template>
                    <el-form label-width="40" label-position="left">
                        <!-- <el-form-item label="标题" v-if="v.title">
                            <van-tag type="success" size="medium">@{ v.title }</van-tag>
                        </el-form-item> -->
                        <el-form-item label="IP">
                            <van-tag type="success" size="medium">@{ v.ip }</van-tag>
                        </el-form-item>
                        <el-form-item label="品牌" v-show="v.brand">
                            <van-tag type="success" size="medium">@{ v.brand }</van-tag>
                        </el-form-item>
                        <el-form-item label="类型">
                            <van-tag type="success" size="medium">@{ v.type }</van-tag>
                        </el-form-item>
                        <el-form-item label="角色">
                            <el-row>
                                <van-tag
                                    type="success"
                                    style="margin: 0 5px 5px 0;"
                                    v-for="(vv, kk) in v.character"
                                    :key="kk">
                                    @{ vv }
                                </van-tag>
                            </el-row>
                        </el-form-item>
                        <el-form-item label="图片">
                            <el-row>
                                <el-image
                                    style="width: 100px; height: 100px; margin: 0 5px 5px 0; border-radius: 10px;"
                                    v-for="(vv, kk) in v.img"
                                    :key="kk"
                                    :src="imageTemplate.replaceAll('{url}', vv)"
                                    mode="cover"
                                    :preview-src-list="v.img.map(vvv => imageTemplate.replaceAll('{url}', vvv))"
                                    hide-on-click-modal
                                    :initial-index="kk"
                                    loading="lazy">
                                </el-image>
                            </el-row>
                        </el-form-item>
                    </el-form>

                    <template #footer>
                        <van-button style="margin-right: 5px;" size="small" type="success" plain>复制添加</van-button>
                        <van-button style="margin-right: 5px;" size="small" type="success" @click="handleEdit(v)">编辑</van-button>
                        <van-button style="margin-right: 5px;" size="small" type="success" @click="handleAddImage(v.id)">上传图片</van-button>
                        <van-button style="margin-right: 5px;" size="small" type="danger">删除</van-button>
                    </template>
                </el-card>
                <el-row style="margin-top: 20px;">
                    <input type="file" ref="uploadFileRef" id="input-file" multiple style="display: none;" @change="handleFileChange">
                    <van-button type="success" @click="onAdd">添加</van-button>
                </el-row>

                <van-pagination
                    style="margin-top: 20px;"
                    v-model="page"
                    :items-per-page="10"
                    :total-items="count"
                    force-ellipses
                    prev-text="<"
                    next-text=">"
                    @change="handlePageChange">
                </van-pagination>
            </el-main>

            <!-- 添加/编辑 浮层 -->
            <van-popup
                v-model:show="edit.show"
                position="bottom"
                :style="{ height: '80%' }">
                <el-container>
                    <el-header>
                        <span>@{ edit.id > 0 ? '编辑' : '添加' }</span>
                    </el-header>
                    <van-cell-group inset>
                        <van-cell>
                            <van-field v-model="edit.data.title" label="标题" placeholder="输入标题" clearable/>
                        </van-cell>
                        <van-cell>
                            <van-field v-model="edit.data.ip" label="IP" placeholder="输入 IP" clearable @input="ipCompleteShow = true"/>
                        </van-cell>                        
                        <van-cell v-show="ipAutoComplele.length > 0 && ipCompleteShow">
                            <div>
                                <van-tag
                                    v-for="(v, k) in ipAutoComplele"
                                    :key="k"
                                    type="success"
                                    @click="ipCompleteShow = !(edit.data.ip = v.label)"
                                    style="margin-left: 10px;">
                                    @{ v.label }
                                </van-tag>
                                <van-icon style="margin-left: 10px;" name="cross" @click="ipCompleteShow = false"/>
                            </div>
                        </van-cell>
                        <van-cell>
                            <van-field v-model="edit.data.brand" label="品牌" placeholder="输入品牌" clearable @input="brandCompleteShow= true"/>
                        </van-cell>                        
                        <van-cell v-show="brandAutoComplele.length > 0 && brandCompleteShow">
                            <div>
                                <van-tag
                                    v-for="(v, k) in brandAutoComplele"
                                    :key="k"
                                    type="success"
                                    @click="brandCompleteShow = !(edit.data.brand = v.label)"
                                    style="margin-left: 10px;">
                                    @{ v.label }
                                </van-tag>
                                <van-icon style="margin-left: 10px;" name="cross" @click="brandCompleteShow = false"/>
                            </div>
                        </van-cell>
                        <van-cell>
                            <van-field v-model="edit.data.type" label="类型" placeholder="输入类型" clearable @input="typeCompleteShow= true"/>
                        </van-cell>                        
                        <van-cell v-show="typeAutoComplele.length > 0 && typeCompleteShow">
                            <div>
                                <van-tag
                                    v-for="(v, k) in typeAutoComplele"
                                    :key="k"
                                    type="success"
                                    @click="typeCompleteShow = !(edit.data.type = v.label)"
                                    style="margin-left: 10px;">
                                    @{ v.label }
                                </van-tag>
                                <van-icon style="margin-left: 10px;" name="cross" @click="typeCompleteShow = false"/>
                            </div>
                        </van-cell>
                        <van-cell>
                            <van-field v-model="edit.data.character" label="角色" placeholder="输入角色" clearable @input="characterCompleteShow = true"/>
                        </van-cell>                        
                        <van-cell v-show="characterAutoComplele.length > 0 && characterCompleteShow">
                            <div>
                                <van-tag
                                    v-for="(v, k) in characterAutoComplele"
                                    :key="k"
                                    type="success"
                                    @click="characterCompleteShow = !(edit.data.character = v.label)"
                                    style="margin-left: 10px;">
                                    @{ v.label }
                                </van-tag>
                                <van-icon style="margin-left: 10px;" name="cross" @click="characterCompleteShow = false"/>
                            </div>
                        </van-cell>
                        <div class="add-images">
                            <div
                                class="add-image-item"
                                v-for="(v, k) in edit.data.img"
                                :key="k">
                                <div v-show="v.status !== 'done'">@{ fileStatusTextMap(v.status) }</div>
                                <van-image
                                    v-show="v.status === 'done'"
                                    :src="imageTemplate.replaceAll('{url}', v.url)"
                                    fit="cover"
                                    :radius="10"
                                    :width="100"
                                    :height="100"
                                    @click="handleImageClick(v.name)">
                                </van-image>
                            </div>
                        </div>
                        <van-action-sheet
                            v-model:show="edit.imageActionSheet.show"
                            :actions="edit.imageActionSheet.actions"
                            cancel-text="取消"
                            close-on-click-action
                            @select="handleImageActionSelect">
                        </van-action-sheet>
                        <van-button style="margin-top: 10px;" type="success" size="small" @click="handleAddImage">添加图片</van-button>
                    </van-cell-group>
                    <van-button style="width: 90%; margin-left: 5%; margin-top: 10px;" size="big" type="success" @click="handleSave">保存</van-button>
                </el-container>
            </van-popup>
          </el-container>
        <el-header>

        <van-back-top />

    </div>
    <script>
        const { createApp, ref, onMounted, computed } = Vue

        const app = createApp({
            setup() {
                const list = ref([])
                const count = ref(Number('{{ count | safe }}'))
                const page = ref(1)
                const search = ref({
                    ip: [],
                    type: [],
                    brand: [],
                    character: []
                })
                
                const getParams = (key, dft = '') => {
                    const params = new URLSearchParams(window.location.search)
                    if (!key) {
                        return params
                    }
                    return params.get(key) || dft
                }

                const imageTemplate = ref('https://image-space.thebestxt.cc/website/{url}')
                // const imageTemplate = ref('http://localhost:1234/compress-{url}')

                const optionsSearch = ref({
                    ip: '',
                    brand: '',
                    type: '',
                    character: ''
                })
                const options = ref({
                    ip: [],
                    brand: [],
                    type: [],
                    character: [],
                })
                
                const buildOptions = key => {
                    return [...new Set([
                        ...options.value[key].filter(v => !optionsSearch.value[key] || v.label.indexOf(optionsSearch.value[key]) > -1),
                        ...search.value[key].map(v => ({ label: v, value: v }))
                    ].map(v => v.label))].map(v => ({ label: v, value: v }))
                }
                const ipOptins = computed(() => buildOptions('ip'))
                const brandOptins = computed(() => buildOptions('brand'))
                const typeOptins = computed(() => buildOptions('type'))
                const characterOptins = computed(() => buildOptions('character'))

                const handleClearSearch = () => {
                    if (
                        search.value.ip.length === 0 &&
                        search.value.type.length === 0 &&
                        search.value.brand.length === 0 &&
                        search.value.character.length === 0
                    ) {
                        vant.showToast('没有搜索条件，不用清空')
                        return
                    }
                    const params = getParams()
                    params.delete('ip')
                    params.delete('type')
                    params.delete('brand')
                    params.delete('character')
                    params.set('page', 1)
                    location.href = `/goods/dashboard?${params.toString()}`
                }
                const handleSearch = () => {
                    const params = getParams()
                    params.set('page', 1)
                    if (search.value.ip.length > 0) {
                        params.set('ip', search.value.ip.join(','))
                    }
                    if (search.value.type.length > 0) {
                        params.set('type', search.value.type.join(','))
                    }
                    if (search.value.brand.length > 0) {
                        params.set('brand', search.value.brand.join(','))
                    }
                    if (search.value.character.length > 0) {
                        params.set('character', search.value.character.join(','))
                    }

                    location.href = `/goods/dashboard?${params.toString()}`
                }

                const handlePageChange = () => {
                    const params = getParams()
                    params.set('page', page.value)
                    location.href = `/goods/dashboard?${params.toString()}`
                }

                const initSearch = () => {
                    const params = getParams()
                    const ip = params.get('ip')
                    if (ip) {
                        search.value.ip = ip.split(',')
                    }
                    const type = params.get('type')
                    if (type) {
                        search.value.type = type.split(',')
                    }
                    const brand = params.get('brand')
                    if (brand) {
                        search.value.brand = brand.split(',')
                    }
                    const character = params.get('character')
                    if (character) {
                        search.value.character = character.split(',')
                    }
                }

                const edit = ref({
                    show: false,
                    loading: false,
                    id: 0,
                    data: {
                        title: '',
                        ip: '',
                        brand: '',
                        type: '',
                        character: '',
                        img: [
                            {
                                name: '',
                                url: 'goods/103-1736229877609.jpg',
                                status: 'done',
                            },
                            {
                                name: '',
                                url: 'goods/103-1736229877609.jpg',
                                status: 'done', // compress upload done error
                            }
                        ],
                    },
                    imageActionSheet: {
                        show: false,
                        name: '',
                        actions: [
                            { key: 'preview', name: '预览' },
                            { key: 'delete', name: '删除' }
                        ]
                    }
                })
                const onAdd = () => {
                    edit.value.show = true
                }

                const buildAutoComplete = key => {
                    return [...new Set(options.value[key].filter(v => edit.value.data[key] && v.label.indexOf(edit.value.data[key]) > -1))]
                }
                const ipAutoComplele = computed(() => buildAutoComplete('ip'))
                const ipCompleteShow = ref(true)
                const brandAutoComplele = computed(() => buildAutoComplete('brand'))
                const brandCompleteShow = ref(true)
                const typeAutoComplele = computed(() => buildAutoComplete('type'))
                const typeCompleteShow = ref(true)
                const characterAutoComplele = computed(() => buildAutoComplete('character'))
                const characterCompleteShow = ref(true)

                const handleEditAddImage = () => {
                    document.querySelector('#input-file').click()
                }

                const handleEdit = v => {
                    edit.value.show = true
                }

                const uploadFileRef = ref(null)
                const uploadFile = ref({
                    id: 0
                })
                const handleAddImage = id => {
                    uploadFile.value.id = id
                    document.querySelector('#input-file').click()
                }
                const fileStatusTextMap = key => {
                    const map = {
                        unbegin: '未开始',
                        compress: '压缩中',
                        uploading: '上传中',
                        done: '完成',
                        error: '错误',
                    }
                    return map[key]
                }
                const handleFileChange = () => {
                    const files = [...document.querySelector('#input-file').files]
                    console.log('files', files)
                    files.forEach(v => {
                        edit.value.data.img.push({
                            name: v.name,
                            url: '',
                            status: 'compress', // compress uploading done error
                        })
                        fileZip(v).then(blob => {
                            const findFile = edit.value.data.img.find(vv => vv.name == v.name)
                            console.log('findFile', findFile)
                            
                            if (findFile)  {
                                findFile.status = 'uploading'
                            }

                            postFile(blobToFile(blob, findFile.name)).then(res => {
                                console.log('上传完毕', res)
                                const uploadRes = JSON.parse(res)
                                findFile.name = uploadRes.data.data.data.key.split('/').at(-1)
                                findFile.url = uploadRes.data.data.data.key.replaceAll('website/', '')
                                findFile.status = 'done'
                            })
                        }).catch(err => {
                            console.error('fileZip.err', err)
                            const findFile = edit.value.data.img.find(vv => vv.name = v.name)
                            findFile.status = 'error'
                        }).finally(() => {
                            console.log('finally')
                            document.querySelector('#input-file').value = ''
                        })
                    })
                }

                function blobToFile(blob, fileName) {
                    return new File([blob], fileName, { type: blob.type });
                }
                const postFile = file => {
                    return new Promise((resolve, reject) => {
                        // 创建 FormData
                        const formData = new FormData()

                        formData.append('files', file, file.name)

                        // 创建 XMLHttpRequest
                        const xhr = new XMLHttpRequest()

                        // 配置请求
                        xhr.open('POST', '/goods/upload', true);

                        // 监听上传进度
                        xhr.upload.onprogress = (event) => {
                            if (event.lengthComputable) {
                                const progress = Math.round((event.loaded / event.total) * 100);
                                console.log(`Uploading: ${progress}%`);
                            }
                        };

                        // 监听请求完成
                        xhr.onload = () => {
                            if (xhr.status === 200) {
                                console.log('Upload successful:', xhr.responseText);
                                resolve(xhr.responseText)
                            } else {
                                reject(xhr.statusText)
                                console.error('Upload failed:', xhr.statusText);
                            }
                        };

                        // 监听请求错误
                        xhr.onerror = (e) => {
                            console.error('Upload error occurred.', e);
                        };

                        // 发送请求
                        xhr.send(formData);
                    })
                }

                const fileZip = file => {
                    const picaa = new pica()
                    
                    return new Promise((resolve, reject) => {
                        const img = document.createElement('img')
                        img.src = URL.createObjectURL(file)

                        img.onload = async () => {
                            
                            const canvas = document.createElement('canvas');
                            canvas.width = img.width / 2; // 压缩至原图的 50%
                            canvas.height = img.height / 2;

                            const result = await picaa.resize(img, canvas, {
                                quality: 3,
                            })

                            const compressNum = file.size / 102400
                            console.log('compressNum', compressNum)
                            
                            picaa.toBlob(result, 'image/jpeg', compressNum).then((blob) => {
                                resolve(blob)
                            }).catch(err => {
                                console.error('picaa.toBlob.err', err)
                                
                                reject(err)
                            })
                        }
                    })
                }

                const handleImageClick = name => {
                    const findFile = edit.value.data.img.find(v => v.name == name)
                    if (findFile) {
                        edit.value.imageActionSheet.name = name
                        edit.value.imageActionSheet.show = true
                    }
                }
                const handleImageActionSelect = (e) => {
                    console.log('handleImageActionSelect.e', e)
                    const findFileIndex = edit.value.data.img.findIndex(v => v.name == edit.value.imageActionSheet.name)

                    switch (e.key) {
                        case 'preview': {
                            vant.showImagePreview({
                                images: edit.value.data.img.map(v => imageTemplate.value.replaceAll('{url}', v.url)),
                                startPosition: findFileIndex,
                            })
                        } break
                        case 'delete': {
                            edit.value.data.img.splice(findFileIndex, 1)
                        } break
                    }
                }

                const handleSave = () => {
                    console.log('edit.data', edit.value.data)
                    edit.value.show = false
                }
                onMounted(() => {
                    list.value = [...JSON.parse(`{{ goodsJson | safe }}`)]
                    page.value = Number(getParams('page', 1))
                    options.value = {...JSON.parse(`{{ options | safe }}`)}
                    initSearch()
                })

                return {
                    search,
                    list,
                    imageTemplate,
                    optionsSearch,
                    options,
                    ipOptins,
                    brandOptins,
                    characterOptins,
                    typeOptins,
                    onAdd,
                    handleFileChange,
                    handleAddImage,
                    page,
                    count,
                    edit,
                    handlePageChange,
                    handleSearch,
                    handleClearSearch,
                    ipAutoComplele,
                    brandAutoComplele,
                    typeAutoComplele,
                    characterAutoComplele,
                    ipCompleteShow,
                    brandCompleteShow,
                    typeCompleteShow,
                    characterCompleteShow,
                    handleEdit,
                    fileStatusTextMap,
                    handleImageClick,
                    handleImageActionSelect,
                    handleSave
                }
            }
        })
        
        app.config.compilerOptions.delimiters = ['@{', '}']
        app.use(ElementPlus)
        app.use(vant)
        app.use(vant.Lazyload)
        app.mount('#app')
    </script>
</body>
</html>